﻿using System;
using System.Reflection;
using System.Linq;
using System.Collections.Generic;
using System.Net.Http;
using System.ComponentModel.DataAnnotations;

namespace DotNetStar.OpenSDK
{
    /// <summary>
    /// OpenSDK 的扩展。
    /// </summary>
    public static class SdkExtensions
    {
        /// <summary>
        /// 获取当前 <see cref="ApiRequest{TResponse}"/> 对象的所有属性并返回一个 <see cref="KeyValueCollection"/> 对象。
        /// </summary>
        /// <remarks>
        /// 若属性标记了 <see cref="ArgumentIgnoreAttribute"/> 特性，将不导入；
        /// 若属性标记了 <see cref="ArgumentNameAttribute"/> 特性，将作为 key 的名称。
        /// </remarks>
        /// <typeparam name="TResponse">响应类型。</typeparam>
        /// <param name="request">请求的扩展实例。</param>
        /// <returns>从属性中获取的参数集合。</returns>
        /// <exception cref="System.ArgumentNullException">request 不能是 null。</exception>
        public static KeyValueCollection GetArguments<TResponse>(this ApiRequest<TResponse> request)
        {
            if (request == null)
            {
                throw new ArgumentNullException(nameof(request));
            }

            return new KeyValueCollection(request);
        }

        /// <summary>
        /// 获取调用了 <see cref="ApiRequest{TResponse}.OnArgumentsCreating(dynamic)"/>  方法后重组过的参数。
        /// </summary>
        /// <typeparam name="TResponse">响应类型。</typeparam>
        /// <param name="request">请求的扩展实例。</param>
        /// <returns>由 <see cref="ApiRequest{TResponse}"/> 派生类重写了 <see cref="ApiRequest{TResponse}.OnArgumentsCreating(dynamic)"/> 决定的返回值。</returns>
        public static dynamic GetCreatingArguments<TResponse>(this ApiRequest<TResponse> request)
            => request.OnArgumentsCreating(request.GetArguments());

        /// <summary>
        /// 获取当前请求标记的 <see cref="HttpMethodAttribute"/> 派生的特性。
        /// 若无标记该特性，则默认使用 <see cref="HttpGetAttribute"/> 特性作为 HTTP 的请求方式。
        /// </summary>
        /// <typeparam name="TResponse">响应类型。</typeparam>
        /// <param name="request">请求的扩展实例。</param>
        /// <returns>派生自 <see cref="HttpMethodAttribute"/> 的特性或默认的 <see cref="HttpGetAttribute"/> 特性。</returns>
        public static HttpMethodAttribute GetHttpMethod<TResponse>(this ApiRequest<TResponse> request)
        => request.GetType().GetCustomAttributes<HttpMethodAttribute>().FirstOrDefault() ?? new HttpGetAttribute();

        /// <summary>
        /// 获取当前请求的 <see cref="HttpContentTypeAttribute"/> 派生的特性。
        /// 若无标记该特性，则默认使用 <see cref="HttpContentTypeAttribute"/> 特性的 text/plain 作为 HTTP 的内容类型。
        /// </summary>
        /// <typeparam name="TResponse">响应类型。</typeparam>
        /// <param name="request">请求的扩展实例。</param>
        /// <returns>派生自 <see cref="HttpContentTypeAttribute"/> 的特性或默认的 <see cref="HttpContentTypeAttribute"/> 特性。</returns>
        public static HttpContentTypeAttribute GetContentType<TResponse>(this ApiRequest<TResponse> request)
        => request.GetType().GetCustomAttributes<HttpContentTypeAttribute>().FirstOrDefault() ?? new HttpContentTypeAttribute("text/plain");

        /// <summary>
        /// 校验指定的请求对象。
        /// </summary>
        /// <typeparam name="TResponse">响应类型。</typeparam>
        /// <param name="request">要校验的请求。</param>
        /// <param name="throwIfInvalid">一个布尔值，表示校验失败是否抛出异常。</param>
        /// <exception cref="SdkException">对象校验失败。</exception>
        /// <returns>校验结果的字符串。若校验成功，则为空字符串。</returns>
        public static string VerifyApiRequest<TResponse>(this ApiRequest<TResponse> request, bool throwIfInvalid = true)
        {
            var result = new List<ValidationResult>();
            if (!Validator.TryValidateObject(request, new ValidationContext(request), result))
            {
                var message = result.Select(m => m.ErrorMessage).Aggregate((pre, next) => $"{pre}{Environment.NewLine}{next}");
                if (throwIfInvalid)
                {
                    throw new SdkException(message);
                }
                return message;
            }
            return string.Empty;
        }

        /// <summary>
        /// 获取响应的内容字符串。
        /// </summary>
        /// <param name="httpResponse">The HTTP response.</param>
        /// <returns></returns>
        public static string GetContentAsString(this HttpResponseMessage httpResponse) => httpResponse.Content.ReadAsStringAsync().GetAwaiter().GetResult();

    }
}
